package core;

import java.util.*;

/**
 * A Specie is stoichiometrically distinct chemical entity that is
 * present in the system.  It is defined by stoichiometry and a
 * formation constant K, which can optionally be calculated from
 * <code>deltaH</code> and <code>deltaS</code>.  As an example, acetic
 * acid and acetate are two different species.
 *
 * @author Charlie Moore
 */
public class Specie extends ChemicalEntity {

    /**
     * List of components and their stoichiometric number to form this
     * specie.  For example, OH<sup>-</sup> would have ['H2O': -1,
     * 'H': 1] as its components.
     */
    private HashMap<Component, Integer> components = new HashMap<Component, Integer>();
    
    /**
     * A specie can be any of these types
     */
    public enum SpecieType { 
        /** 
         * A dissolved specie
         */
        DISSOLVED,
        /**
         * An ionic or molecular solid
         */
        SOLID,
        /** 
         * In the gas phase
         */
        GAS, 
        /**
         * A solid surface.
         */
        SURFACE,
        /**
         * Adsobed specie on surface
         */
        SORBED,
        /**
         * The specie type is unknown
         */
        UNKNOWN;

        public String toString() {
            return name().toLowerCase();
        }

        public static SpecieType getValue(String value) {
            try {
                return valueOf(value.toUpperCase());
            } catch (Exception e) {
                return SpecieType.UNKNOWN;
            }
        }
    }
    
    /** 
     * The specific type of this specie
     */
    private SpecieType type;
    
    /**
     * log(C) - log of the equilibrium concetration
     */
    private double logC;
    
    /**
     * log(K) - log of the formation constant
     */
    private double logK;

    /**
     * Enthalpy of formation (kJ/mol)
     */
    private double deltaH;

    /**
     * Entropy of formation
     */
    private double deltaS;

    /**
     * Equilibrium that can be passed in to identify the Specie.  The
     * Equilibrium parses the equation and then the Specie can inspect
     * the components and coefficients parsed out to determine if it's
     * the same Specie.
     */
    private Equilibrium equation;

    /**
     * Gibbs free energy (kJ/mol)
     */
    private double deltaG;
    
    //=================================
    // Constructors
    //---------------------------------
    public Specie(){}
    public Specie(String newName) {
        setName(newName);
    }
    
    //=================================
    // Getters and Setters
    //---------------------------------
    
    public HashMap<Component, Integer> getComponents() { return components; }
    public void addComponent(Component comp, Integer num) {
        components.put(comp, num);
        setChanged();
        notifyObservers();
    }
    
    public SpecieType getType() { return type; }
    public static Vector<String> getTypes() {
        Vector<String> types = new Vector<String>();
        
        for (SpecieType s : SpecieType.values()) {
            types.add(s.toString());
        }
        return types;
    }
    public void setType(SpecieType newType) {
        type = newType;
        setChanged();
        notifyObservers();
    }
    public void setType(String name) {
        setType(SpecieType.getValue(name));
   }

    public String getEquation() {
        return equation.getEquation();
    }

    public void setEqilibrium(String eq) {
        equation = new Equilibrium(eq, java.lang.Math.exp(logK));
    }
    
    public double getLogC() { return logC; }
    public void   setLogC(double newLogC) {
        logC = newLogC;
        setChanged();
        notifyObservers();
    }
    
    public double getLogK() { return logK; }
    public void   setLogK(double newLogK) {
        logK = newLogK;
        setChanged();
        notifyObservers();
    }
    
    public double getDeltaH() { return deltaH; }
    public void   setDeltaH(double newDeltaH) {
        deltaH = newDeltaH;
        setChanged();
        notifyObservers();
    }
    
    public double getDeltaS() { return deltaS; }
    public void   setDeltaS(double newDeltaS) {
        deltaS = newDeltaS;
        setChanged();
        notifyObservers();
    }
    
    public double getDeltaG() { return deltaG; }
    public void   setDeltaG(double newDeltaG) {
        deltaG = newDeltaG;
        setChanged();
        notifyObservers();
    }
    
    /**
     * Returns the Specie's data as an XML string.
     */
    public String toString()
    {
        StringBuilder data = new StringBuilder();
        data.append("\t<specie>\n\t\t<name>");
        data.append(getName());
        data.append("</name>\n\t\t<type>");
        data.append(getType());
        data.append("</type>\n\t\t<deltaH>");
        data.append(getDeltaH());
        data.append("</deltaH>\n\t\t<deltaS>");
        data.append(getDeltaS());
        data.append("</deltaS>\n\t\t<deltaG>");
        data.append(getDeltaG());
        data.append("</deltaG>\n\t\t<logK>");
        data.append(getLogK());
        data.append("</logK>\n\t\t<std_dev>");
        data.append(getStdDev());
        data.append("</std_dev>\n\t</specie>");
        
        return data.toString();
    }
}
